package tests;

import static org.junit.Assert.*;
import org.junit.Test;
import MULE.Model.Game;
import MULE.Model.Player;
import MULE.Model.Logic.GameLogic;

/**
 * Test cases for GameLogic.nextPlayer method.
 * 
 * @author Jeremy Stephens
 */
public class NextPlayerTests {

	/**
	 * Instantiates a new GameLogic object whose associated Game object has
	 * specified number of players.
	 * 
	 * @param numPlayers
	 *            Number of players for GameLogic's Game object.
	 * @return New GameLogic that has Game object with given number of players.
	 * @throws Exception
	 *             Specified number of players is invalid.
	 */
	public GameLogic createGameLogicWithNumPlayers(int numPlayers)
			throws Exception {
		Game g = createGameWithIdOrderedPlayers(numPlayers);
		return new GameLogic(null, g);
	}

	/**
	 * Creates and returns a new game object with the specified number of
	 * players ordered by player id starting at zero.
	 * 
	 * @param numPlayers
	 *            The number of players the game should have.
	 * @return A new game object with the specified number of players, in id
	 *         order.
	 * @throws Exception
	 *             Specified number of players is invalid.
	 */
	public Game createGameWithIdOrderedPlayers(int numPlayers) throws Exception {
		// Create new game object.
		Game gameObject = new Game(numPlayers, Game.Difficulty.STANDARD, null);
		// Populate the player list with test players.
		// Players are in id number order starting with 0.
		int playerId = 0;
		for (int i = 0; i < numPlayers; i++) {
			gameObject.addPlayer(createTestPlayer(playerId++));
		}
		return gameObject;
	}

	/**
	 * Creates and returns a new player object used for testing. Player is
	 * assigned specified id.
	 * 
	 * @param playerId
	 *            The test player's id.
	 * @return New player object.
	 */
	public Player createTestPlayer(int playerId) {
		return new Player(playerId, "", null, null);
	}

	/**
	 * Test case: zero players in game.
	 */
	@Test
	public void testZeroPlayersInGame() {
		try {
			// Create game with zero players.
			GameLogic gameLogic = createGameLogicWithNumPlayers(0);
			// Make first call.
			Player nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be null.", null, nextPlayer);
		} catch (Exception e) {
			// Should not throw exception.
			fail(e.getMessage());
		}
	}

	/**
	 * Test case: less than minimum (one) players in game.
	 */
	@Test
	public void testLowerInvalidPartition() {
		try {
			// Create game with less than minimum allowed (one) players.
			GameLogic gameLogic = createGameLogicWithNumPlayers(Game.MIN_NUM_PLAYERS - 1);
			// Make first call.
			Player nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0.", 0,
					nextPlayer.getId());
			gameLogic.setCurrentPlayer(nextPlayer);
			// Next call should return same player.
			nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0 (wrap around).", 0,
					nextPlayer.getId());
		} catch (Exception e) {
			// Should not throw exception.
			fail(e.getMessage());
		}
	}

	/**
	 * Test case: minimum (two) players in game.
	 */
	@Test
	public void testLowerBoundary() {
		try {
			// Create game with allowed number (two) players.
			GameLogic gameLogic = createGameLogicWithNumPlayers(Game.MIN_NUM_PLAYERS);
			// First call should return player 0.
			Player nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0.", 0,
					nextPlayer.getId());
			gameLogic.setCurrentPlayer(nextPlayer);
			// Next call should return player 1.
			nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 1.", 1,
					nextPlayer.getId());
			gameLogic.setCurrentPlayer(nextPlayer);
			// Next call should return player 0.
			nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0 (wrap around).", 0,
					nextPlayer.getId());
		} catch (Exception e) {
			// Should not throw exception.
			fail(e.getMessage());
		}
	}

	/**
	 * Test case: allowed number (three) players in game.
	 */
	@Test
	public void testValidPartition() {
		try {
			// Create game with allowed number (three) of players.
			int numPlayers = Game.MIN_NUM_PLAYERS + 1;
			GameLogic gameLogic = createGameLogicWithNumPlayers(numPlayers);
			// Make sure next player return players in order.
			for (int i = 0; i < numPlayers; ++i) {
				Player nextPlayer = gameLogic.nextPlayer();
				assertEquals("Next player should be player " + i + ".", i,
						nextPlayer.getId());
				gameLogic.setCurrentPlayer(nextPlayer);
			}
			// Make sure wrap around works.
			Player nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0 (wrap around).", 0,
					nextPlayer.getId());
		} catch (Exception e) {
			// Should not throw exception.
			fail(e.getMessage());
		}
	}

	/**
	 * Test case: maximum (four) players in game.
	 */
	@Test
	public void testUpperBoundary() {
		try {
			// Create game with maximum allowed number (four) players.
			GameLogic gameLogic = createGameLogicWithNumPlayers(Game.MAX_NUM_PLAYERS);
			// Make sure next player return players in order.
			for (int i = 0; i < Game.MAX_NUM_PLAYERS; ++i) {
				Player nextPlayer = gameLogic.nextPlayer();
				assertEquals("Next player should be player " + i + ".", i,
						nextPlayer.getId());
				gameLogic.setCurrentPlayer(nextPlayer);
			}
			// Make sure wrap around works.
			Player nextPlayer = gameLogic.nextPlayer();
			assertEquals("Next player should be player 0 (wrap around).", 0,
					nextPlayer.getId());
		} catch (Exception e) {
			// Should not throw exception.
			fail(e.getMessage());
		}
	}

	/**
	 * Test case: more than maximum (five) players in game. Game object does not
	 * allow this case -- exception will be thrown before being able to call
	 * GameLogic.nextPlayer.
	 */
	@Test(expected = Exception.class)
	public void testUpperInvalidPartition() throws Exception {
		// Create game with more than maximum allowed number (five) players.
		int numPlayers = Game.MAX_NUM_PLAYERS + 1;
		// Should throw exception...
		createGameLogicWithNumPlayers(numPlayers);

	}
}
